# install.packages("tidyverse");
# install.packages("rgdal");
library(tidyverse)
require("maps")
library(geosphere)
library(stringr)
library(rgdal)
library(caret)
library(lubridate)
if (!require(ggmap)) { install.packages('ggmap'); require(ggmap) }
path.to.csv <- '../Year_911_Data.csv'
spd.911 <- read.csv(path.to.csv, TRUE)
spd.911$clearance_date_ts = as.POSIXct(strptime(spd.911$Event.Clearance.Date, "%m/%d/%Y %I:%M:%S %p"))
spd.911$clearance_date_date = as.Date(spd.911$clearance_date_ts)
# View(spd.911)
# path to the FOLDER with the .shp file in it. the second param is the name of the .shp file
# seattle <- readOGR(dsn = path.expand("~/documents/INFO370/project-teamname-v2/maps-api-test"), layer = "Seattle_City_Limits")
# usa <- map_data("state")
# data <- merge(usa, spd.911)
# Red Square coordinates
here_long <- -122.3095
here_lat <- 47.6560
seattle = get_map(location = c(here_long, here_lat), zoom = 13, maptype = 'roadmap')
Map from URL : http://maps.googleapis.com/maps/api/staticmap?center=47.656,-122.3095&zoom=13&size=640x640&scale=2&maptype=roadmap&language=en-EN&sensor=false
freq_by_desc <- table(droplevels(data$Event.Clearance.Description))
# View(freq_by_desc)
ggplot(as.data.frame(freq_by_desc),
aes(x = Var1, y = Freq)) +
geom_bar(stat = 'identity') +# create bar plot
coord_flip()
#Traffic related calls, suspicious circumstances, and disturbances are the the most significant threats to pedestrations
ggmap(seattle) +
geom_point(data = data, aes(x = Longitude, y = Latitude, group = Event.Clearance.Description, color = Event.Clearance.Description), alpha = 0.5) +
facet_wrap(~ Event.Clearance.Description) +
theme(axis.ticks = element_blank(),
axis.text.x = element_blank(),
axis.text.y = element_blank(),
legend.position = "none"
)
# selecting just ID and location data
df_loc <- data %>% dplyr::select(CAD.CDW.ID, Longitude, Latitude)
# figuring out number of clusters
wss <- c()
# clusters 1 to 15
for (i in 1:15) {
wss[i] <- sum(kmeans(df_loc, centers=i)$withinss)
}
plot(1:15, wss, type="b", xlab="Number of Clusters",
ylab="Within groups sum of squares")
# fitting model
fit <- kmeans(df_loc, 10)
fit$centers # look at cluster sizes and means. want clusters to be about equal size
fit$cluster
cluster.size <- data.frame(1:10, fit$size)
cluster.size
ggplot(data = cluster.size, aes(x = X1.10, y = fit.size)) +
geom_bar(stat = 'identity')
ggplot()
ggmap(seattle) +
geom_point(data = as.data.frame(fit$centers), aes(x = Longitude, y = Latitude), alpha = 0.5)
# looking at cluster means
aggregate(df_loc, by=list(fit$cluster), FUN=mean)
df_loc
# adding data back into dataframe
# df_loc <- df_loc %>% mutate(cluster = fit$cluster)
data$cluster <- fit$cluster
# View(data)
# timestamp -> year month day hour minute
# sector -> to factor (there are 17 sectors)
# beat -> to factor (there are 3 beats per sector)
# clean the data a bit more
data$event_clearance_ts = as.POSIXct(strptime(data$Event.Clearance.Date, "%m/%d/%Y %I:%M:%S %p"))
data$event_clearance_date = as.Date(data$event_clearance_ts)
data$event_clearance_month = month(ymd_hms(as.character(data$event_clearance_ts)))
data$event_clearance_day = weekdays(data$event_clearance_date)
data$event_clearance_hr = hour(ymd_hms(as.character(data$event_clearance_ts)))
data$event_clearance_mn = minute(ymd_hms(as.character(data$event_clearance_ts)))
data$Initial.Type.Group = factor(data$Initial.Type.Group)
data$Event.Clearance.Group = factor(data$Event.Clearance.Group)
data$Zone.Beat = factor(data$Zone.Beat)
data$District.Sector = factor(data$District.Sector)
data$event_clearance_day = factor(data$event_clearance_day)
data
col.names <- paste(c(
"Event.Clearance.Code"
, "cluster"
, "Census.Tract"
, "event_clearance_day"
, "Event.Clearance.Group"
, "Event.Clearance.SubGroup"
, "District.Sector"
, "Zone.Beat"
#, "event_clearance_ts"
# ,"Incident.Location"
, "event_clearance_hr"
, "event_clearance_mn"
, "event_clearance_month"
, "Hundred.Block.Location"
), collapse="|")
cols <- grep(col.names, colnames(data))
cols
# corr_matrix <- cor(data[,cols]) # correlations between all predictor vars
# corr_matrix
# cutoff <- 0.5 # should be higher in practice
# highly_corr <- findCorrelation(corr_matrix, cutoff=cutoff)
# print(colnames(spd.911)[highly_corr]) # age is highly correalted with pregnant
train.data <- select(data, cols)
train.data
# data <- data %>% droplevels()
# grep("Hundred.Block.Location", colnames(train.data), invert = T)
predictors <- grep("Hundred.Block.Location", colnames(train.data), invert = T)
outcome <- grep("Hundred.Block.Location", colnames(train.data))
# train.data[,predictors]
frame <- data.frame(train.data[,predictors])
frame
out.factor <- train.data$Hundred.Block.Location
as.vector(out.factor)
control <- rfeControl(functions = rfFuncs, method="cv", number=10)
results <- rfe(frame, out.factor, sizes = c(1:13), rfeControl = control) # this will take AWHILE...
results
ggplot(results)
# chosen features
predictors(results)
LS0tDQp0aXRsZTogIlIgTm90ZWJvb2siDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQpgYGB7ciBzZXR1cH0NCiMgaW5zdGFsbC5wYWNrYWdlcygidGlkeXZlcnNlIik7DQojIGluc3RhbGwucGFja2FnZXMoInJnZGFsIik7DQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCnJlcXVpcmUoIm1hcHMiKQ0KbGlicmFyeShnZW9zcGhlcmUpDQpsaWJyYXJ5KHN0cmluZ3IpDQpsaWJyYXJ5KHJnZGFsKQ0KbGlicmFyeShjYXJldCkNCmxpYnJhcnkobHVicmlkYXRlKQ0KaWYgKCFyZXF1aXJlKGdnbWFwKSkgeyBpbnN0YWxsLnBhY2thZ2VzKCdnZ21hcCcpOyByZXF1aXJlKGdnbWFwKSB9DQpwYXRoLnRvLmNzdiA8LSAnLi4vWWVhcl85MTFfRGF0YS5jc3YnDQpzcGQuOTExIDwtIHJlYWQuY3N2KHBhdGgudG8uY3N2LCBUUlVFKQ0KDQpzcGQuOTExJGNsZWFyYW5jZV9kYXRlX3RzID0gYXMuUE9TSVhjdChzdHJwdGltZShzcGQuOTExJEV2ZW50LkNsZWFyYW5jZS5EYXRlLCAiJW0vJWQvJVkgJUk6JU06JVMgJXAiKSkNCnNwZC45MTEkY2xlYXJhbmNlX2RhdGVfZGF0ZSA9IGFzLkRhdGUoc3BkLjkxMSRjbGVhcmFuY2VfZGF0ZV90cykNCiMgVmlldyhzcGQuOTExKQ0KDQoNCg0KIyBwYXRoIHRvIHRoZSBGT0xERVIgd2l0aCB0aGUgLnNocCBmaWxlIGluIGl0LiB0aGUgc2Vjb25kIHBhcmFtIGlzIHRoZSBuYW1lIG9mIHRoZSAuc2hwIGZpbGUNCiMgc2VhdHRsZSA8LSByZWFkT0dSKGRzbiA9IHBhdGguZXhwYW5kKCJ+L2RvY3VtZW50cy9JTkZPMzcwL3Byb2plY3QtdGVhbW5hbWUtdjIvbWFwcy1hcGktdGVzdCIpLCBsYXllciA9ICJTZWF0dGxlX0NpdHlfTGltaXRzIikNCg0KIyB1c2EgPC0gbWFwX2RhdGEoInN0YXRlIikNCiMgZGF0YSA8LSBtZXJnZSh1c2EsIHNwZC45MTEpDQojIFJlZCBTcXVhcmUgY29vcmRpbmF0ZXMNCmhlcmVfbG9uZyA8LSAgLTEyMi4zMDk1DQpoZXJlX2xhdCA8LSA0Ny42NTYwDQoNCnNlYXR0bGUgPSBnZXRfbWFwKGxvY2F0aW9uID0gYyhoZXJlX2xvbmcsIGhlcmVfbGF0KSwgem9vbSA9IDEzLCBtYXB0eXBlID0gJ3JvYWRtYXAnKQ0KDQpgYGANCg0KDQpgYGB7cn0NCnNwZC45MTEgPC0gc3BkLjkxMSAlPiUgDQogICAgICAgICAgICAgcm93d2lzZSgpICU+JSANCiAgICAgICAgICAgICBtdXRhdGUoZGlzdD1kaXN0VmluY2VudHlFbGxpcHNvaWQoYyhMb25naXR1ZGUsIExhdGl0dWRlKSwgYyhoZXJlX2xvbmcsIGhlcmVfbGF0KSkpICAgICAgICAgICAgICANCm5yb3coc3BkLjkxMSkNCg0KZGVzY3JpcHRpb25zIDwtIGMoIlNUUk9ORyBBUk0gUk9CQkVSWSIsICJQRVJTT04gV0lUSCBBIFdFQVBPTiAoTk9UIEdVTikiLCAiSEFaQVJEUyIsICJIQVJBU1NNRU5ULCBUSFJFQVRTIiwgIkZJR0hUIERJU1RVUkJBTkNFIiwgIkNSSVNJUyBDT01QTEFJTlQgLSBHRU5FUkFMIiwgIkFSTUVEIFJPQkJFUlkiKQ0KDQojIFJlbW92ZXMgU3BlY2lmaWNhbGx5IEhhcmFzc21lbnQgYnkgVGVsZXBob25lIGFuZCBXcml0aW5nLCBhcyB3ZWxsIGFzIG90aGVyIG5vbi1zY2FyeSBjcmltZXMNCmRhdGEucGVkIDwtIHNwZC45MTEgJT4lIGZpbHRlcihzdHJfZGV0ZWN0KEV2ZW50LkNsZWFyYW5jZS5EZXNjcmlwdGlvbiwgcGFzdGUoZGVzY3JpcHRpb25zLCBjb2xsYXBzZT0ifCIpKSkgJT4lIGZpbHRlcighc3RyX2RldGVjdChFdmVudC5DbGVhcmFuY2UuRGVzY3JpcHRpb24sICJIQVJBU1NNRU5ULCBUSFJFQVRTIC0gQlkgVEVMRVBIT05FLCBXUklUSU5HIikpDQojIGRhdGEucGVkIDwtIGRhdGEubm93DQpucm93KGRhdGEucGVkKQ0KDQpkYXRhLm5vdyA8LSBkYXRhLnBlZCAlPiUgZmlsdGVyKGNsZWFyYW5jZV9kYXRlX3RzIDwgJzIwMTctMTAtMzEgMDA6MDA6MDAnKQ0KbnJvdyhkYXRhLm5vdykNCiAgICAgICAgICAgICAgICAgIA0KZGF0YS5oZXJlIDwtIGRhdGEubm93ICU+JSBmaWx0ZXIoZGlzdCA8IDQ2MDApDQoNCmRhdGEgPC0gZGF0YS5oZXJlDQpucm93KGRhdGEpDQojIFZpZXcoZGF0YSkNCg0KZ2dtYXAoc2VhdHRsZSkgKw0KICAgZ2VvbV9wb2ludChkYXRhID0gZGF0YSwgYWVzKHggPSBMb25naXR1ZGUsIHkgPSBMYXRpdHVkZSksIGNvbG91ciA9ICJyZWQiLCBhbHBoYSA9IDAuNzUpDQogICNjb29yZF9tYXAoKQ0KDQpgYGANCg0KYGBge3J9DQpmcmVxX2J5X2Rlc2MgPC0gdGFibGUoZHJvcGxldmVscyhkYXRhJEV2ZW50LkNsZWFyYW5jZS5EZXNjcmlwdGlvbikpDQojIFZpZXcoZnJlcV9ieV9kZXNjKQ0KDQpnZ3Bsb3QoYXMuZGF0YS5mcmFtZShmcmVxX2J5X2Rlc2MpLCANCiAgICAgICBhZXMoeCA9IFZhcjEsIHkgPSBGcmVxKSkgKw0KICAgICAgIGdlb21fYmFyKHN0YXQgPSAnaWRlbnRpdHknKSArIyBjcmVhdGUgYmFyIHBsb3QNCiAgICBjb29yZF9mbGlwKCkNCg0KI1RyYWZmaWMgcmVsYXRlZCBjYWxscywgc3VzcGljaW91cyBjaXJjdW1zdGFuY2VzLCBhbmQgZGlzdHVyYmFuY2VzIGFyZSB0aGUgdGhlIG1vc3Qgc2lnbmlmaWNhbnQgdGhyZWF0cyB0byBwZWRlc3RyYXRpb25zDQoNCiAgICAgICAgDQpgYGANCg0KYGBge3J9DQpnZ21hcChzZWF0dGxlKSArDQogIGdlb21fcG9pbnQoZGF0YSA9IGRhdGEsIGFlcyh4ID0gTG9uZ2l0dWRlLCB5ID0gTGF0aXR1ZGUsIGdyb3VwID0gRXZlbnQuQ2xlYXJhbmNlLkRlc2NyaXB0aW9uLCBjb2xvciA9IEV2ZW50LkNsZWFyYW5jZS5EZXNjcmlwdGlvbiksIGFscGhhID0gMC41KSArDQogIGZhY2V0X3dyYXAofiBFdmVudC5DbGVhcmFuY2UuRGVzY3JpcHRpb24pICsNCiAgdGhlbWUoYXhpcy50aWNrcyA9IGVsZW1lbnRfYmxhbmsoKSwgDQogICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF9ibGFuaygpLA0KICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiDQogICAgICAgICkNCmBgYA0KDQpgYGB7cn0NCiMgc2VsZWN0aW5nIGp1c3QgSUQgYW5kIGxvY2F0aW9uIGRhdGENCmRmX2xvYyA8LSBkYXRhICU+JSBkcGx5cjo6c2VsZWN0KENBRC5DRFcuSUQsIExvbmdpdHVkZSwgTGF0aXR1ZGUpDQoNCiMgZmlndXJpbmcgb3V0IG51bWJlciBvZiBjbHVzdGVycw0Kd3NzIDwtIGMoKQ0KIyBjbHVzdGVycyAxIHRvIDE1DQpmb3IgKGkgaW4gMToxNSkgew0KICB3c3NbaV0gPC0gc3VtKGttZWFucyhkZl9sb2MsIGNlbnRlcnM9aSkkd2l0aGluc3MpDQp9DQpwbG90KDE6MTUsIHdzcywgdHlwZT0iYiIsIHhsYWI9Ik51bWJlciBvZiBDbHVzdGVycyIsDQogIHlsYWI9IldpdGhpbiBncm91cHMgc3VtIG9mIHNxdWFyZXMiKQ0KDQojIGZpdHRpbmcgbW9kZWwNCmZpdCA8LSBrbWVhbnMoZGZfbG9jLCAxMCkNCmZpdCRjZW50ZXJzICMgbG9vayBhdCBjbHVzdGVyIHNpemVzIGFuZCBtZWFucy4gd2FudCBjbHVzdGVycyB0byBiZSBhYm91dCBlcXVhbCBzaXplDQpmaXQkY2x1c3Rlcg0KY2x1c3Rlci5zaXplIDwtIGRhdGEuZnJhbWUoMToxMCwgZml0JHNpemUpDQpjbHVzdGVyLnNpemUNCg0KZ2dwbG90KGRhdGEgPSBjbHVzdGVyLnNpemUsIGFlcyh4ID0gWDEuMTAsIHkgPSBmaXQuc2l6ZSkpICsNCiAgZ2VvbV9iYXIoc3RhdCA9ICdpZGVudGl0eScpDQpnZ3Bsb3QoKQ0KZ2dtYXAoc2VhdHRsZSkgKw0KICBnZW9tX3BvaW50KGRhdGEgPSBhcy5kYXRhLmZyYW1lKGZpdCRjZW50ZXJzKSwgYWVzKHggPSBMb25naXR1ZGUsIHkgPSBMYXRpdHVkZSksIGFscGhhID0gMC41KQ0KIyBsb29raW5nIGF0IGNsdXN0ZXIgbWVhbnMNCmFnZ3JlZ2F0ZShkZl9sb2MsIGJ5PWxpc3QoZml0JGNsdXN0ZXIpLCBGVU49bWVhbikNCg0KZGZfbG9jDQoNCiMgYWRkaW5nIGRhdGEgYmFjayBpbnRvIGRhdGFmcmFtZSANCiMgZGZfbG9jIDwtIGRmX2xvYyAlPiUgbXV0YXRlKGNsdXN0ZXIgPSBmaXQkY2x1c3RlcikgDQpkYXRhJGNsdXN0ZXIgPC0gZml0JGNsdXN0ZXINCg0KIyBWaWV3KGRhdGEpDQpgYGANCg0KYGBge3J9DQojIHRpbWVzdGFtcCAtPiAgeWVhciAgbW9udGggZGF5IGhvdXIgIG1pbnV0ZQ0KIyBzZWN0b3IgLT4gdG8gZmFjdG9yICh0aGVyZSBhcmUgMTcgc2VjdG9ycykNCiMgYmVhdCAtPiB0byBmYWN0b3IgKHRoZXJlIGFyZSAzIGJlYXRzIHBlciBzZWN0b3IpDQoNCiMgY2xlYW4gdGhlIGRhdGEgYSBiaXQgbW9yZQ0KZGF0YSRldmVudF9jbGVhcmFuY2VfdHMgPSBhcy5QT1NJWGN0KHN0cnB0aW1lKGRhdGEkRXZlbnQuQ2xlYXJhbmNlLkRhdGUsICIlbS8lZC8lWSAlSTolTTolUyAlcCIpKQ0KZGF0YSRldmVudF9jbGVhcmFuY2VfZGF0ZSA9IGFzLkRhdGUoZGF0YSRldmVudF9jbGVhcmFuY2VfdHMpDQpkYXRhJGV2ZW50X2NsZWFyYW5jZV9tb250aCA9IG1vbnRoKHltZF9obXMoYXMuY2hhcmFjdGVyKGRhdGEkZXZlbnRfY2xlYXJhbmNlX3RzKSkpDQpkYXRhJGV2ZW50X2NsZWFyYW5jZV9kYXkgPSB3ZWVrZGF5cyhkYXRhJGV2ZW50X2NsZWFyYW5jZV9kYXRlKQ0KZGF0YSRldmVudF9jbGVhcmFuY2VfaHIgPSBob3VyKHltZF9obXMoYXMuY2hhcmFjdGVyKGRhdGEkZXZlbnRfY2xlYXJhbmNlX3RzKSkpDQpkYXRhJGV2ZW50X2NsZWFyYW5jZV9tbiA9IG1pbnV0ZSh5bWRfaG1zKGFzLmNoYXJhY3RlcihkYXRhJGV2ZW50X2NsZWFyYW5jZV90cykpKQ0KZGF0YSRJbml0aWFsLlR5cGUuR3JvdXAgPSBmYWN0b3IoZGF0YSRJbml0aWFsLlR5cGUuR3JvdXApDQpkYXRhJEV2ZW50LkNsZWFyYW5jZS5Hcm91cCA9IGZhY3RvcihkYXRhJEV2ZW50LkNsZWFyYW5jZS5Hcm91cCkNCmRhdGEkWm9uZS5CZWF0ID0gZmFjdG9yKGRhdGEkWm9uZS5CZWF0KQ0KZGF0YSREaXN0cmljdC5TZWN0b3IgPSBmYWN0b3IoZGF0YSREaXN0cmljdC5TZWN0b3IpDQpkYXRhJGV2ZW50X2NsZWFyYW5jZV9kYXkgPSBmYWN0b3IoZGF0YSRldmVudF9jbGVhcmFuY2VfZGF5KQ0KZGF0YQ0KDQpjb2wubmFtZXMgPC0gcGFzdGUoYygNCiAgIkV2ZW50LkNsZWFyYW5jZS5Db2RlIg0KICAsICJjbHVzdGVyIg0KICAsICJDZW5zdXMuVHJhY3QiDQogICwgImV2ZW50X2NsZWFyYW5jZV9kYXkiDQogICwgIkV2ZW50LkNsZWFyYW5jZS5Hcm91cCINCiAgLCAiRXZlbnQuQ2xlYXJhbmNlLlN1Ykdyb3VwIg0KICAsICJEaXN0cmljdC5TZWN0b3IiDQogICwgIlpvbmUuQmVhdCINCiAgIywgImV2ZW50X2NsZWFyYW5jZV90cyINCiAgIyAsIkluY2lkZW50LkxvY2F0aW9uIg0KICAsICJldmVudF9jbGVhcmFuY2VfaHIiDQogICwgImV2ZW50X2NsZWFyYW5jZV9tbiINCiAgLCAiZXZlbnRfY2xlYXJhbmNlX21vbnRoIiANCiAgLCAiSHVuZHJlZC5CbG9jay5Mb2NhdGlvbiINCiAgKSwgY29sbGFwc2U9InwiKQ0KY29scyA8LSBncmVwKGNvbC5uYW1lcywgY29sbmFtZXMoZGF0YSkpDQpjb2xzDQojIGNvcnJfbWF0cml4IDwtIGNvcihkYXRhWyxjb2xzXSkgIyBjb3JyZWxhdGlvbnMgYmV0d2VlbiBhbGwgcHJlZGljdG9yIHZhcnMNCiMgY29ycl9tYXRyaXgNCg0KIyBjdXRvZmYgPC0gMC41ICMgc2hvdWxkIGJlIGhpZ2hlciBpbiBwcmFjdGljZQ0KDQojIGhpZ2hseV9jb3JyIDwtIGZpbmRDb3JyZWxhdGlvbihjb3JyX21hdHJpeCwgY3V0b2ZmPWN1dG9mZikNCiMgcHJpbnQoY29sbmFtZXMoc3BkLjkxMSlbaGlnaGx5X2NvcnJdKSAjIGFnZSBpcyBoaWdobHkgY29ycmVhbHRlZCB3aXRoIHByZWduYW50DQoNCnRyYWluLmRhdGEgPC0gc2VsZWN0KGRhdGEsIGNvbHMpDQp0cmFpbi5kYXRhDQojIGRhdGEgPC0gZGF0YSAlPiUgZHJvcGxldmVscygpDQoNCiMgZ3JlcCgiSHVuZHJlZC5CbG9jay5Mb2NhdGlvbiIsIGNvbG5hbWVzKHRyYWluLmRhdGEpLCBpbnZlcnQgPSBUKQ0KDQpwcmVkaWN0b3JzIDwtIGdyZXAoIkh1bmRyZWQuQmxvY2suTG9jYXRpb24iLCBjb2xuYW1lcyh0cmFpbi5kYXRhKSwgaW52ZXJ0ID0gVCkNCm91dGNvbWUgPC0gZ3JlcCgiSHVuZHJlZC5CbG9jay5Mb2NhdGlvbiIsIGNvbG5hbWVzKHRyYWluLmRhdGEpKQ0KDQojIHRyYWluLmRhdGFbLHByZWRpY3RvcnNdDQpmcmFtZSA8LSBkYXRhLmZyYW1lKHRyYWluLmRhdGFbLHByZWRpY3RvcnNdKQ0KZnJhbWUNCm91dC5mYWN0b3IgPC0gdHJhaW4uZGF0YSRIdW5kcmVkLkJsb2NrLkxvY2F0aW9uDQphcy52ZWN0b3Iob3V0LmZhY3RvcikNCg0KDQpjb250cm9sIDwtIHJmZUNvbnRyb2woZnVuY3Rpb25zID0gcmZGdW5jcywgbWV0aG9kPSJjdiIsIG51bWJlcj0xMCkNCnJlc3VsdHMgPC0gcmZlKGZyYW1lLCBvdXQuZmFjdG9yLCBzaXplcyA9IGMoMToxMyksIHJmZUNvbnRyb2wgPSBjb250cm9sKSAjIHRoaXMgd2lsbCB0YWtlIEFXSElMRS4uLg0KDQpyZXN1bHRzDQpnZ3Bsb3QocmVzdWx0cykNCg0KIyBjaG9zZW4gZmVhdHVyZXMNCnByZWRpY3RvcnMocmVzdWx0cykNCmBgYA0K